#!/usr/bin/python
import json
import pprint
import urllib

import gdata.books.service

class GoogleBooksExtractor:
    @classmethod
    def lookupGBooksByString(cls, string, max, offset):
        service = gdata.books.service.BookService()
        service.ssl = False
        json_str = service.Get('http://www.google.com/books/feeds/volumes?alt=json&q=%s&max-results=%s&start-index=%s' % (string,max,int(offset) + 1), converter=str)

        data = json.loads(json_str)
        if len(data["feed"]["entry"]) == 0:
            return ""

        pprint.PrettyPrinter().pprint([cls.parseGBooksItem(entry) for entry in data["feed"]["entry"]])
        items = filter(lambda x:x["isbn"]!="", [cls.parseGBooksItem(entry) for entry in data["feed"]["entry"]])
        pprint.PrettyPrinter().pprint(items)
        return items

    @classmethod 
    def lookupGBooksByIsbn(cls, isbn):
        service = gdata.books.service.BookService()
        service.ssl = False
        json_str = service.Get('http://www.google.com/books/feeds/volumes?alt=json&q=isbn:%s' % isbn, converter=str)

        data = json.loads(json_str)
        if len(data["feed"]["entry"]) == 0:
            return ""

        info = cls.parseGBooksItem(data["feed"]["entry"][0])
        pprint.PrettyPrinter().pprint(info)
        return info

    @classmethod
    def parseGBooksItem(cls, info):
        if "dc$creator" in info:
            authors = map(lambda x:x["$t"], info["dc$creator"])
        else:
            authors = ""

        if "dc$description" in info:
            description = info["dc$description"][0]["$t"]
        else:
            description = ""
        
        identifiers = map(lambda x:x["$t"],info["dc$identifier"])
        isbns = filter(lambda x:len(x)==18 and x[:4].upper()=='ISBN',identifiers)
        if len(isbns) > 0:
            isbn = isbns[0][5:]
        else:
            isbn = ""

        if "dc$publisher" in info:
            publisher = info["dc$publisher"][0]["$t"]
        else:
            publisher = ""
        title = ": ".join(map(lambda x:x["$t"], info["dc$title"]))

        return {"authors":authors,
                "description":description,
                "isbn":isbn,
                "publisher":publisher,
                "title":title}

class GoogleExtractor:
    @classmethod
    def composeTotalInfo(cls, bookData, productData):
        attributes = {
                "title":bookData["title"],
                "description":bookData["description"],
                "purchase_url":productData["product"]["link"],
                "price_market":min(productData["product"]["inventories"],
                                   key=lambda x:x["price"])["price"],
                "isbn":bookData["isbn"],
                "publisher":bookData["publisher"],
                "authors":bookData["authors"]}

        if "images" in productData["product"]:
            attributes["image_url"] = productData[
                    "product"]["images"][0]["link"]
        
        return {"type":"book","attributes": \
                dict(filter(lambda (k,v):v!="" and v!=[], attributes.items()))}
   

    @classmethod
    def extractFromShopping(cls, gtins):
        sock = urllib.urlopen(
                "https://www.googleapis.com/shopping/search/v1/public/products?key=AIzaSyDB00if3zMjkwxIErvmhDz11iWKrWogRNY&country=US&maxResults=100&startIndex=1&restrictBy=gtin=0%s" % \
                "|0".join(gtins))
        data =json.loads(sock.read())
        sock.close()
        return data

    @classmethod
    def isbn10To13(isbn10):
        isbn = "978" + isbn
        isbn = isbn[:-1] + `(10-sum([int(isbn[i])*(1+2*(i%2))
                                     for i in range(12)])%10)%10`
        return isbn
    
    
    @classmethod
    def extractGoogle(cls, urlvars):
        #Assume type is GET
        if 'isbn' in urlvars and 'max' in urlvars and 'offset' in urlvars:
            isbn = urlvars['isbn']
            if len(isbn) == 10:
                # Converts isbn 10 to isbn 13, which can be used as gtin.
                cls.isbn10To13(isbn)
    
            item = GoogleBooksExtractor.lookupGBooksByIsbn(isbn)
            data = cls.extractFromShopping([isbn])
            if "items" not in data:
                return HttpResponse('0')

            cheapestProduct = cls.getCheapestProduct(data["items"])
            resultjson = cls.composeTotalInfo(item,cheapestProduct)
            return json.dumps([resultjson])
   

        elif 'string' in urlvars and 'max' in urlvars and 'offset' in urlvars:
            books = GoogleBooksExtractor.lookupGBooksByString(
                            urlvars["string"], int(urlvars['max']), 
                            urlvars['offset'])

            isbns = list(set(list(map(lambda x:x['isbn'], books))))
            
            data = cls.extractFromShopping(isbns)
                    
            if "items" not in data:
                return HttpResponse('0')
    
            results = []
            for isbn in isbns:
                products = filter(lambda x:x["product"]["gtin"][1:] == isbn,
                                  data["items"])
                if len(products) > 0:
                    cheapestProduct = cls.getCheapestProduct(products)
                    info = filter(lambda x:x["isbn"] == isbn, books)[0]
                    results.append(cls.composeTotalInfo(info, cheapestProduct))
    
            return json.dumps(results)
        
        elif 'limit' in urlvars:
            return '100'
    
        return '0'

    @classmethod
    def getCheapestProduct(cls, items):
        return min(items,key=lambda x: min(x["product"]["inventories"], 
                                           key=lambda y:y["price"]))
 
#GoogleExtractor.lookupGBooksByString("pat the bunny")
#GoogleExtractor.lookupGBooksByIsbn("0307120007")
#print GoogleExtractor.extractGoogle({"string":"pat the bunny","max":3,
#                                    "offset":1})
